/*
 * Created on 2006-4-16
 *
 */
package org.iori.util.xml;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.apache.xml.serialize.OutputFormat;
import org.apache.xml.serialize.XMLSerializer;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;

/**
 * The util class for XML file
 * 
 * @author zhangjianbin
 * @history
 *         <P>
 *         2006-3-30 Created Zhang Jianbin
 * 
 */
public class XMLUtil {
	/**
	 * Parses the xml file and return the Document object
	 * 
	 * @param filename
	 *            the absolute filename of the xml file
	 * @throws Exception
	 *             if error occurs
	 */
	public static Document parseDocument(String filename) throws Exception {
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		DocumentBuilder builder = factory.newDocumentBuilder();
		InputStream is = new FileInputStream(filename);
		Document doc = null;

		try {
			doc = builder.parse(is);
		} finally {
			if (null != is)
				is.close();
		}
		return doc;
	}

	/**
	 * Returns the first child element with specified node name of the parent
	 * elemnt
	 * 
	 * @return the first found element, if not found retur null
	 * 
	 */
	public static Element getFirstChildElementByName(Element parentElement,
			String childElementName) {

		// Get the children
		NodeList childNodeList = parentElement
				.getElementsByTagName(childElementName);

		// Iterator them
		for (int i = 0; i < childNodeList.getLength(); i++) {
			Node child = childNodeList.item(i);

			if (child.getNodeType() == Node.ELEMENT_NODE
					&& child.getNodeName().equals(childElementName)) {
				return (Element) child;
			}
		}

		return null;
	}

	/**
	 * Returns the first element which matchs the node name and the
	 * attribute(name-value)
	 * 
	 * @param parentElement
	 *            parent element
	 * @param childElementName
	 *            specifies the chile element node name
	 * @param attrName
	 *            specifies the atrribute name
	 * @param attrValue
	 *            specifies the vaue the attribute name
	 * @return the found element, if not found return null
	 */
	public static Element getFirstChildElementByNameAndSpecAttr(
			Element parentElement, String childElementName, String attrName,
			String attrValue) {

		// Get the children
		NodeList childNodeList = parentElement
				.getElementsByTagName(childElementName);

		// Iterator them to find
		for (int i = 0; i < childNodeList.getLength(); i++) {
			Node child = childNodeList.item(i);

			if (child.getNodeType() == Node.ELEMENT_NODE
					&& child.getNodeName().equals(childElementName)
					&& attrValue.equals(getElementAttrValue((Element) child,
							attrName))) {

				return (Element) child;
			}
		}

		return null;
	}

	/**
	 * Returns the attribute value of the element
	 * 
	 * @param elemnt
	 *            the element with the attrbiute
	 * @param attrName
	 *            specifies the attribute name
	 * @return the attribute value, if the element does not have the attribute,
	 *         return zero-length string
	 */
	public static String getElementAttrValue(Element elemnt, String attrName) {
		NamedNodeMap attrs = elemnt.getAttributes();
		for (int i = 0; i < attrs.getLength(); i++) {
			if (attrName.equals(attrs.item(i).getNodeName())) {
				return attrs.item(i).getNodeValue();
			}
		}

		return "";
	}

	/**
	 * Returns the text in the element. All the text node(include CDATA section)
	 * of this element will be merged to a string
	 * 
	 * @param element
	 *            the specified element
	 * @return the text in the elemnt, if the elemnt no any text node, return
	 *         zero-length string
	 */
	public static String getElementText(Element element) {
		if (null == element) {
			return "";
		}

		StringBuffer textValue = new StringBuffer(256);

		if (element.hasChildNodes()) {
			NodeList children = element.getChildNodes();

			for (int childCount = 0; childCount < children.getLength(); childCount++) {
				Node child = children.item(childCount);

				if (child.getNodeType() == Node.CDATA_SECTION_NODE
						|| child.getNodeType() == Node.TEXT_NODE) {
					textValue.append(child.getNodeValue().trim());
				}
			}
		}

		return textValue.toString();
	}

	/**
	 * Generates a new element. The new element may be with one text node
	 * 
	 * @param doc
	 *            the Document object
	 * @param elementName
	 *            the node name of the new element
	 * @param textValue
	 *            the node value of the text node attached to the new element.
	 * @return a new Elememt object, , If the textValue is null, only the new
	 *         element returned
	 */
	public static Element generateElement(Document doc, String elementName,
			String textValue) {
		Element element = doc.createElement(elementName);
		if (null != textValue) {
			Text textNode = doc.createTextNode(textValue);

			element.appendChild(textNode);
		}

		return element;
	}

	/**
	 * Outputs a xml file with a readable format, each level elemnt is in a new
	 * line and indented by 2 spaces
	 * 
	 * @param doc
	 *            the Document object
	 * @param filename
	 *            the filename writing the xml data
	 * @param encoding
	 *            the xml encoding
	 * @throws Exception
	 *             if error occurs
	 */
	public static void outputFormatedDocument(Document doc, String filename,
			String encoding) throws Exception {

		OutputFormat format = new OutputFormat(doc);
		format.setIndenting(true);
		format.setIndent(2);
		format.setEncoding(encoding);

		FileOutputStream fos = new FileOutputStream(filename);
		try {
			XMLSerializer serializer = new XMLSerializer(fos, format);
			serializer.serialize(doc);
		} finally {
			if (null != fos) {
				fos.close();
			}
		}
	}

	/**
	 * Parses the xml file and return the Document object
	 * 
	 * @param is
	 *            the input stream of the xml data
	 * @throws Exception
	 *             if error occurs
	 */
	public static Document parseDocument(InputStream is) throws Exception {
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		DocumentBuilder builder = factory.newDocumentBuilder();
		Document doc = null;

		try {
			doc = builder.parse(is);
		} finally {
			if (null != is)
				is.close();
		}
		return doc;
	}

}